"""
系统监控脚本
实时监控系统性能指标和健康状态
"""

import psutil
import time
import json
import logging
import asyncio
import aiohttp
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, asdict
import sqlite3
import os


@dataclass
class SystemMetrics:
    """系统指标数据类"""
    timestamp: str
    cpu_percent: float
    memory_percent: float
    memory_used_mb: float
    memory_available_mb: float
    disk_percent: float
    disk_used_gb: float
    disk_free_gb: float
    network_sent_mb: float
    network_recv_mb: float
    process_count: int
    load_average: List[float]
    gpu_memory_used_mb: float = 0
    gpu_utilization_percent: float = 0


@dataclass
class ApplicationMetrics:
    """应用指标数据类"""
    timestamp: str
    response_time_ms: float
    active_connections: int
    request_count: int
    error_count: int
    database_connections: int
    cache_hit_rate: float
    ai_tasks_running: int
    ai_tasks_queued: int


class SystemMonitor:
    """系统监控器"""
    
    def __init__(self, config: Dict[str, Any]):
        self.config = config
        self.logger = self._setup_logging()
        self.db_path = config.get("db_path", "monitoring.db")
        self.api_base_url = config.get("api_base_url", "http://localhost:8000")
        self.monitoring_interval = config.get("monitoring_interval", 30)  # 秒
        self.alert_thresholds = config.get("alert_thresholds", {})
        self.is_running = False
        
        # 初始化数据库
        self._init_database()
        
        # 网络统计基线
        self.network_baseline = psutil.net_io_counters()
    
    def _setup_logging(self) -> logging.Logger:
        """设置日志"""
        logger = logging.getLogger("SystemMonitor")
        logger.setLevel(logging.INFO)
        
        # 文件处理器
        file_handler = logging.FileHandler("system_monitor.log")
        file_handler.setLevel(logging.INFO)
        
        # 控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        
        # 格式化器
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)
        
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        
        return logger
    
    def _init_database(self):
        """初始化监控数据库"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 系统指标表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS system_metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                cpu_percent REAL,
                memory_percent REAL,
                memory_used_mb REAL,
                memory_available_mb REAL,
                disk_percent REAL,
                disk_used_gb REAL,
                disk_free_gb REAL,
                network_sent_mb REAL,
                network_recv_mb REAL,
                process_count INTEGER,
                load_average TEXT,
                gpu_memory_used_mb REAL,
                gpu_utilization_percent REAL
            )
        ''')
        
        # 应用指标表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS application_metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                response_time_ms REAL,
                active_connections INTEGER,
                request_count INTEGER,
                error_count INTEGER,
                database_connections INTEGER,
                cache_hit_rate REAL,
                ai_tasks_running INTEGER,
                ai_tasks_queued INTEGER
            )
        ''')
        
        # 告警记录表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS alert_records (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                alert_type TEXT NOT NULL,
                severity TEXT NOT NULL,
                message TEXT NOT NULL,
                metric_name TEXT,
                metric_value REAL,
                threshold_value REAL,
                resolved BOOLEAN DEFAULT FALSE,
                resolved_at TEXT
            )
        ''')
        
        conn.commit()
        conn.close()
    
    def collect_system_metrics(self) -> SystemMetrics:
        """收集系统指标"""
        # CPU使用率
        cpu_percent = psutil.cpu_percent(interval=1)
        
        # 内存使用情况
        memory = psutil.virtual_memory()
        memory_percent = memory.percent
        memory_used_mb = memory.used / (1024 * 1024)
        memory_available_mb = memory.available / (1024 * 1024)
        
        # 磁盘使用情况
        disk = psutil.disk_usage('/')
        disk_percent = (disk.used / disk.total) * 100
        disk_used_gb = disk.used / (1024 * 1024 * 1024)
        disk_free_gb = disk.free / (1024 * 1024 * 1024)
        
        # 网络使用情况
        network = psutil.net_io_counters()
        network_sent_mb = (network.bytes_sent - self.network_baseline.bytes_sent) / (1024 * 1024)
        network_recv_mb = (network.bytes_recv - self.network_baseline.bytes_recv) / (1024 * 1024)
        
        # 进程数量
        process_count = len(psutil.pids())
        
        # 系统负载
        try:
            load_average = list(psutil.getloadavg())
        except AttributeError:
            # Windows系统没有getloadavg
            load_average = [0.0, 0.0, 0.0]
        
        # GPU指标（如果可用）
        gpu_memory_used_mb = 0
        gpu_utilization_percent = 0
        try:
            import GPUtil
            gpus = GPUtil.getGPUs()
            if gpus:
                gpu = gpus[0]  # 使用第一个GPU
                gpu_memory_used_mb = gpu.memoryUsed
                gpu_utilization_percent = gpu.load * 100
        except ImportError:
            pass
        
        return SystemMetrics(
            timestamp=datetime.now().isoformat(),
            cpu_percent=cpu_percent,
            memory_percent=memory_percent,
            memory_used_mb=memory_used_mb,
            memory_available_mb=memory_available_mb,
            disk_percent=disk_percent,
            disk_used_gb=disk_used_gb,
            disk_free_gb=disk_free_gb,
            network_sent_mb=network_sent_mb,
            network_recv_mb=network_recv_mb,
            process_count=process_count,
            load_average=load_average,
            gpu_memory_used_mb=gpu_memory_used_mb,
            gpu_utilization_percent=gpu_utilization_percent
        )
    
    async def collect_application_metrics(self) -> Optional[ApplicationMetrics]:
        """收集应用指标"""
        try:
            async with aiohttp.ClientSession() as session:
                # 测试API响应时间
                start_time = time.time()
                async with session.get(f"{self.api_base_url}/api/v1/system/health") as response:
                    response_time_ms = (time.time() - start_time) * 1000
                    
                    if response.status != 200:
                        self.logger.warning(f"API健康检查失败: {response.status}")
                        return None
                
                # 获取系统统计信息
                async with session.get(f"{self.api_base_url}/api/v1/system/statistics") as response:
                    if response.status == 200:
                        stats = await response.json()
                        
                        return ApplicationMetrics(
                            timestamp=datetime.now().isoformat(),
                            response_time_ms=response_time_ms,
                            active_connections=stats.get("active_connections", 0),
                            request_count=stats.get("total_requests", 0),
                            error_count=stats.get("error_count", 0),
                            database_connections=stats.get("database_connections", 0),
                            cache_hit_rate=stats.get("cache_hit_rate", 0.0),
                            ai_tasks_running=stats.get("ai_tasks_running", 0),
                            ai_tasks_queued=stats.get("ai_tasks_queued", 0)
                        )
                    else:
                        self.logger.warning(f"获取应用统计失败: {response.status}")
                        return None
                        
        except Exception as e:
            self.logger.error(f"收集应用指标失败: {e}")
            return None
    
    def save_metrics(self, system_metrics: SystemMetrics, app_metrics: Optional[ApplicationMetrics]):
        """保存指标到数据库"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 保存系统指标
        cursor.execute('''
            INSERT INTO system_metrics (
                timestamp, cpu_percent, memory_percent, memory_used_mb, memory_available_mb,
                disk_percent, disk_used_gb, disk_free_gb, network_sent_mb, network_recv_mb,
                process_count, load_average, gpu_memory_used_mb, gpu_utilization_percent
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            system_metrics.timestamp,
            system_metrics.cpu_percent,
            system_metrics.memory_percent,
            system_metrics.memory_used_mb,
            system_metrics.memory_available_mb,
            system_metrics.disk_percent,
            system_metrics.disk_used_gb,
            system_metrics.disk_free_gb,
            system_metrics.network_sent_mb,
            system_metrics.network_recv_mb,
            system_metrics.process_count,
            json.dumps(system_metrics.load_average),
            system_metrics.gpu_memory_used_mb,
            system_metrics.gpu_utilization_percent
        ))
        
        # 保存应用指标
        if app_metrics:
            cursor.execute('''
                INSERT INTO application_metrics (
                    timestamp, response_time_ms, active_connections, request_count,
                    error_count, database_connections, cache_hit_rate,
                    ai_tasks_running, ai_tasks_queued
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            ''', (
                app_metrics.timestamp,
                app_metrics.response_time_ms,
                app_metrics.active_connections,
                app_metrics.request_count,
                app_metrics.error_count,
                app_metrics.database_connections,
                app_metrics.cache_hit_rate,
                app_metrics.ai_tasks_running,
                app_metrics.ai_tasks_queued
            ))
        
        conn.commit()
        conn.close()
    
    def check_alerts(self, system_metrics: SystemMetrics, app_metrics: Optional[ApplicationMetrics]):
        """检查告警条件"""
        alerts = []
        
        # 系统指标告警检查
        thresholds = self.alert_thresholds
        
        # CPU使用率告警
        if system_metrics.cpu_percent > thresholds.get("cpu_percent", 80):
            alerts.append({
                "type": "system",
                "severity": "high" if system_metrics.cpu_percent > 90 else "medium",
                "message": f"CPU使用率过高: {system_metrics.cpu_percent:.1f}%",
                "metric_name": "cpu_percent",
                "metric_value": system_metrics.cpu_percent,
                "threshold_value": thresholds.get("cpu_percent", 80)
            })
        
        # 内存使用率告警
        if system_metrics.memory_percent > thresholds.get("memory_percent", 85):
            alerts.append({
                "type": "system",
                "severity": "high" if system_metrics.memory_percent > 95 else "medium",
                "message": f"内存使用率过高: {system_metrics.memory_percent:.1f}%",
                "metric_name": "memory_percent",
                "metric_value": system_metrics.memory_percent,
                "threshold_value": thresholds.get("memory_percent", 85)
            })
        
        # 磁盘使用率告警
        if system_metrics.disk_percent > thresholds.get("disk_percent", 90):
            alerts.append({
                "type": "system",
                "severity": "high" if system_metrics.disk_percent > 95 else "medium",
                "message": f"磁盘使用率过高: {system_metrics.disk_percent:.1f}%",
                "metric_name": "disk_percent",
                "metric_value": system_metrics.disk_percent,
                "threshold_value": thresholds.get("disk_percent", 90)
            })
        
        # 应用指标告警检查
        if app_metrics:
            # API响应时间告警
            if app_metrics.response_time_ms > thresholds.get("response_time_ms", 1000):
                alerts.append({
                    "type": "application",
                    "severity": "high" if app_metrics.response_time_ms > 3000 else "medium",
                    "message": f"API响应时间过长: {app_metrics.response_time_ms:.1f}ms",
                    "metric_name": "response_time_ms",
                    "metric_value": app_metrics.response_time_ms,
                    "threshold_value": thresholds.get("response_time_ms", 1000)
                })
            
            # 错误率告警
            error_rate = app_metrics.error_count / max(app_metrics.request_count, 1)
            if error_rate > thresholds.get("error_rate", 0.05):
                alerts.append({
                    "type": "application",
                    "severity": "high" if error_rate > 0.1 else "medium",
                    "message": f"错误率过高: {error_rate:.2%}",
                    "metric_name": "error_rate",
                    "metric_value": error_rate,
                    "threshold_value": thresholds.get("error_rate", 0.05)
                })
        
        # 保存告警记录
        if alerts:
            self._save_alerts(alerts)
        
        return alerts
    
    def _save_alerts(self, alerts: List[Dict[str, Any]]):
        """保存告警记录"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        for alert in alerts:
            cursor.execute('''
                INSERT INTO alert_records (
                    timestamp, alert_type, severity, message,
                    metric_name, metric_value, threshold_value
                ) VALUES (?, ?, ?, ?, ?, ?, ?)
            ''', (
                datetime.now().isoformat(),
                alert["type"],
                alert["severity"],
                alert["message"],
                alert["metric_name"],
                alert["metric_value"],
                alert["threshold_value"]
            ))
            
            # 记录日志
            self.logger.warning(f"告警: {alert['message']}")
        
        conn.commit()
        conn.close()
    
    def get_metrics_summary(self, hours: int = 24) -> Dict[str, Any]:
        """获取指标摘要"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 计算时间范围
        start_time = (datetime.now() - timedelta(hours=hours)).isoformat()
        
        # 系统指标摘要
        cursor.execute('''
            SELECT 
                AVG(cpu_percent) as avg_cpu,
                MAX(cpu_percent) as max_cpu,
                AVG(memory_percent) as avg_memory,
                MAX(memory_percent) as max_memory,
                AVG(disk_percent) as avg_disk,
                MAX(disk_percent) as max_disk
            FROM system_metrics 
            WHERE timestamp > ?
        ''', (start_time,))
        
        system_summary = cursor.fetchone()
        
        # 应用指标摘要
        cursor.execute('''
            SELECT 
                AVG(response_time_ms) as avg_response_time,
                MAX(response_time_ms) as max_response_time,
                SUM(error_count) as total_errors,
                SUM(request_count) as total_requests
            FROM application_metrics 
            WHERE timestamp > ?
        ''', (start_time,))
        
        app_summary = cursor.fetchone()
        
        # 告警统计
        cursor.execute('''
            SELECT severity, COUNT(*) as count
            FROM alert_records 
            WHERE timestamp > ? AND resolved = FALSE
            GROUP BY severity
        ''', (start_time,))
        
        alert_stats = {row[0]: row[1] for row in cursor.fetchall()}
        
        conn.close()
        
        return {
            "time_range_hours": hours,
            "system_metrics": {
                "avg_cpu_percent": system_summary[0] if system_summary[0] else 0,
                "max_cpu_percent": system_summary[1] if system_summary[1] else 0,
                "avg_memory_percent": system_summary[2] if system_summary[2] else 0,
                "max_memory_percent": system_summary[3] if system_summary[3] else 0,
                "avg_disk_percent": system_summary[4] if system_summary[4] else 0,
                "max_disk_percent": system_summary[5] if system_summary[5] else 0
            },
            "application_metrics": {
                "avg_response_time_ms": app_summary[0] if app_summary[0] else 0,
                "max_response_time_ms": app_summary[1] if app_summary[1] else 0,
                "total_errors": app_summary[2] if app_summary[2] else 0,
                "total_requests": app_summary[3] if app_summary[3] else 0,
                "error_rate": (app_summary[2] / max(app_summary[3], 1)) if app_summary[2] else 0
            },
            "alert_statistics": alert_stats
        }
    
    async def run_monitoring_cycle(self):
        """运行一次监控周期"""
        try:
            # 收集系统指标
            system_metrics = self.collect_system_metrics()
            
            # 收集应用指标
            app_metrics = await self.collect_application_metrics()
            
            # 保存指标
            self.save_metrics(system_metrics, app_metrics)
            
            # 检查告警
            alerts = self.check_alerts(system_metrics, app_metrics)
            
            # 输出当前状态
            self.logger.info(
                f"监控周期完成 - CPU: {system_metrics.cpu_percent:.1f}%, "
                f"内存: {system_metrics.memory_percent:.1f}%, "
                f"磁盘: {system_metrics.disk_percent:.1f}%"
            )
            
            if app_metrics:
                self.logger.info(
                    f"应用状态 - 响应时间: {app_metrics.response_time_ms:.1f}ms, "
                    f"活跃连接: {app_metrics.active_connections}, "
                    f"AI任务: {app_metrics.ai_tasks_running}运行/{app_metrics.ai_tasks_queued}排队"
                )
            
            if alerts:
                self.logger.warning(f"发现 {len(alerts)} 个告警")
            
        except Exception as e:
            self.logger.error(f"监控周期执行失败: {e}")
    
    async def start_monitoring(self):
        """启动监控"""
        self.is_running = True
        self.logger.info("系统监控已启动")
        
        try:
            while self.is_running:
                await self.run_monitoring_cycle()
                await asyncio.sleep(self.monitoring_interval)
        except KeyboardInterrupt:
            self.logger.info("收到停止信号")
        finally:
            self.is_running = False
            self.logger.info("系统监控已停止")
    
    def stop_monitoring(self):
        """停止监控"""
        self.is_running = False


def load_config(config_file: str = "monitoring_config.json") -> Dict[str, Any]:
    """加载监控配置"""
    default_config = {
        "db_path": "monitoring.db",
        "api_base_url": "http://localhost:8000",
        "monitoring_interval": 30,
        "alert_thresholds": {
            "cpu_percent": 80,
            "memory_percent": 85,
            "disk_percent": 90,
            "response_time_ms": 1000,
            "error_rate": 0.05
        }
    }
    
    if os.path.exists(config_file):
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
                # 合并默认配置
                default_config.update(config)
        except Exception as e:
            print(f"加载配置文件失败，使用默认配置: {e}")
    else:
        # 创建默认配置文件
        with open(config_file, 'w', encoding='utf-8') as f:
            json.dump(default_config, f, indent=2, ensure_ascii=False)
        print(f"已创建默认配置文件: {config_file}")
    
    return default_config


async def main():
    """主函数"""
    config = load_config()
    monitor = SystemMonitor(config)
    
    try:
        await monitor.start_monitoring()
    except KeyboardInterrupt:
        print("\n正在停止监控...")
        monitor.stop_monitoring()


if __name__ == "__main__":
    asyncio.run(main())